//
// Created by 李文龙 on 2019/12/1.
//

#ifndef THREAD_TOOL_TEST_OBJECT_POOL_H
#define THREAD_TOOL_TEST_OBJECT_POOL_H

#include "common.h"
#include <list>
#include <map>
#include <mutex>
#include <memory>
#include <string>

NS_BEGIN

static uint64_t GetTimestampMs() {
    auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
    return ms.count();
}

template <class T>
class ObjectPool{
public:
    constexpr static int kMaxBufferCount = 30;
    ObjectPool(int maxCount = kMaxBufferCount,
               const std::string &tag = "",
               const int shrinkIntervalS=60)
    :max_obj_count_{maxCount},
     tag_{tag},
     shrink_interval_s{shrinkIntervalS}{
        last_shrink_pts_ = GetTimestampMs();
    }
    virtual ~ObjectPool(){
        clearAllObjects();
    }
    std::shared_ptr<T> getObject(){
        std::lock_guard<std::mutex> guard(mutex_);
        // 释放长时间不用的对象
        uint64_t curts = GetTimestampMs();
        if(curts >  last_shrink_pts_ + (shrink_interval_s * 1000)){
            for(auto iter = free_queue_.begin(); iter != free_queue_.end();)
            {
                if (curts > iter->first + (shrink_interval_s * 1000))
                {
                    delete iter->second;
                    iter = free_queue_.erase(iter);
                    create_obj_count_--;
                }else {
                    ++iter;
                }
            }
        }
        std::shared_ptr<T> obj;
        // 空闲队列不为空直接获取
        if(!free_queue_.empty()){
            auto lastIter = std::prev(free_queue_.end());
            T * objptr = lastIter->second;
            free_queue_.erase(lastIter);
            obj.reset(objptr, [&](T *p){
                std::lock_guard<std::mutex> guard(mutex_);
                free_queue_.emplace(std::make_pair(GetTimestampMs(), p));
            });
        }
        if(obj){
            return obj;
        }else{
            if(create_obj_count_ < max_obj_count_){
                obj.reset(new(std::nothrow)T() , [&](T *p){
                    std::lock_guard<std::mutex> guard(mutex_);
                    free_queue_.emplace(std::make_pair(GetTimestampMs(), p));
                });
                create_obj_count_++;
                return obj;
            }
        }
        std::shared_ptr<T> data = std::make_shared<T>();
        return data;
    }

    void clearAllObjects(){
        std::lock_guard<std::mutex> guard(mutex_);
        for (const auto &item:free_queue_) {
            if(item.second){
                delete item.second;
                create_obj_count_--;
            }
        }
        free_queue_.clear();
    }

    int getFreeCount(){
        std::lock_guard<std::mutex> guard(mutex_);
        return free_queue_.size();
    }

    int getUsedCount(){
        return create_obj_count_ - getFreeCount();
    }
private:
    std::mutex mutex_;
    std::multimap<uint64_t , T*> free_queue_;
    int create_obj_count_{0};
    int max_obj_count_;
    // 记录最后一次的收缩时间
    uint64_t last_shrink_pts_;
    // 记录最后一次的收缩间隔, 单位秒
    int shrink_interval_s;
    std::string tag_;
};

NS_END

#endif //THREAD_TOOL_TEST_OBJECT_POOL_H
